package earogya.ehr.medical_records.messages;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.ExtraComponents;
import ca.uhn.hl7v2.model.Group;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.Structure;
import ca.uhn.hl7v2.model.Type;
import ca.uhn.hl7v2.model.v251.datatype.ST;
import ca.uhn.hl7v2.model.v251.group.ADR_A19_QUERY_RESPONSE;
import ca.uhn.hl7v2.model.v251.message.ADR_A19;
import ca.uhn.hl7v2.model.v251.message.QRY_A19;
import ca.uhn.hl7v2.model.v251.segment.DG1;
import ca.uhn.hl7v2.model.v251.segment.MSH;
import ca.uhn.hl7v2.model.v251.segment.NK1;
import ca.uhn.hl7v2.model.v251.segment.OBX;
import ca.uhn.hl7v2.model.v251.segment.PID;
import ca.uhn.hl7v2.model.v251.segment.PV1;
import ca.uhn.hl7v2.model.v251.segment.PV2;
import ca.uhn.hl7v2.model.v251.segment.QRD;
import ca.uhn.hl7v2.parser.Parser;
import ca.uhn.hl7v2.validation.ValidationContext;
import earogya.ehr.medical_records.Guardian;
import earogya.ehr.medical_records.GuardianAddressDetails;
import earogya.ehr.medical_records.GuardianEmailAddressDetails;
import earogya.ehr.medical_records.GuardianTelephoneNumberDetails;
import earogya.ehr.medical_records.HibernateManager;

public class QRY_A19_Handler {
	
	private QRY_A19 qryMsg;
	private HibernateManager hibernate_manager=new HibernateManager();
	
	public QRY_A19_Handler(QRY_A19 qryMsg) {
		this.qryMsg=qryMsg;
	}

	public String handleMessage() throws HL7Exception, IOException{
		String response=null;
		response=identifyRequiredInformation();
		return response;
	}
	
	private String identifyRequiredInformation() throws HL7Exception, IOException{
		QRD qrd=qryMsg.getQRD();
		String ack=null;
		String sentValue=qrd.getWhoSubjectFilter(0).getIDNumber().getValue();
		String required_field=qrd.getWhatSubjectFilter(0).getText().getValue();
		if(required_field.equals("guardian")){
			int value=Integer.parseInt(sentValue);
			ack=this.createResponseForGuardian(value);
		}
		else if(required_field.equals("bht")){
			int value=Integer.parseInt(sentValue);
			ack=this.createResponseBhtDetails(value);
		}
		else if(required_field.equals("old admissions")){
			int value=Integer.parseInt(sentValue);
			ack=this.createResponseAdmissionDetails(value, "out");
		}
		else if(required_field.equals("occupied beds")){
			int value=Integer.parseInt(sentValue);
			ack=this.createResponseForOccupiedBeds(value);
		}
		else if(required_field.equals("admission")){
			String[] wardAndBed=sentValue.split(",");
			int patient_id=this.getPatientID(Integer.parseInt(wardAndBed[0]), Integer.parseInt(wardAndBed[1]));
			ack=this.createResponseAdmissionDetails(patient_id, "in");
		}
		return ack;
	}
	
	private String createResponseForGuardian(int patient_id) throws HL7Exception, IOException{
		String message=null;
		ADR_A19 msg=new ADR_A19();
		ADR_A19_QUERY_RESPONSE response=msg.insertQUERY_RESPONSE(0);

		Object[][] fieldsAndValuesAdmission={{"patient_id", patient_id}, {"status", "in"}};
		int guardian_id=hibernate_manager.queryValuesFromDatabase(fieldsAndValuesAdmission, "guardian_id", "Admission", "admission");
		if(guardian_id!=-1){
			Object[][] fieldsAndValues={{"guardian_id", guardian_id}};
			String[] requiredFieldsGuardian={"prefix", "given_name", "further_names", "surname", "nic_no"};
			String[] required_fieldAddress={ "house_number", "street_name", "city_1", "city_2", "country"};
			String[] required_fieldTelephone={"telephone_number"};
			String[] required_fieldEmail={ "email_address"};
			List guardian=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, requiredFieldsGuardian, "Guardian", "guardian");
			List guardianAddressDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldAddress, "GuardianAddressDetails", "guardian_address_detials");
			List guardianEmailAddressDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldEmail, "GuardianEmailAddressDetails", "guardian_email_address_detials");
			List guardianTelephoneNumberDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldTelephone, "GuardianTelephoneNumberDetails", "guardian_telephone_number_details");
			String guardianRelationship=this.getGuardianRelationship(guardian_id, patient_id);
			
			MSH mshSegment = msg.getMSH();
			mshSegment.getFieldSeparator().setValue("|");
			mshSegment.getEncodingCharacters().setValue("^~\\&");
			mshSegment.getDateTimeOfMessage().getTime().setValue(Calendar.getInstance().getTime());
			mshSegment.getVersionID().getVersionID().setValue("2.5.1");
			mshSegment.getMessageType().getMsg1_MessageCode().setValue("ADR");
			mshSegment.getMessageType().getTriggerEvent().setValue("A19");
			
			NK1 nk1=response.getNK1();
			Object[] guardianDetails=(Object[])guardian.get(0);
			nk1.getNK1Name(0).getPrefixEgDR().setValue((String)guardianDetails[0]);
			nk1.getNK1Name(0).getGivenName().setValue((String)guardianDetails[1]);
			nk1.getNK1Name(0).getSecondAndFurtherGivenNamesOrInitialsThereof().setValue((String)guardianDetails[2]);
			nk1.getNK1Name(0).getFamilyName().getSurname().setValue((String)guardianDetails[3]);
			nk1.getContactPersonSocialSecurityNumber().setValue((String)guardianDetails[4]);
			
			for(int i=0; i<guardianAddressDetails.size(); i++){
				Object[] guardianAD=(Object[])guardianAddressDetails.get(i);
				nk1.getAddress(i).getStreetAddress().getDwellingNumber().setValue((String)guardianAD[0]);
				nk1.getAddress(i).getStreetAddress().getStreetName().setValue((String)guardianAD[1]);
				nk1.getAddress(i).getOtherDesignation().setValue((String)guardianAD[2]);
				nk1.getAddress(i).getCity().setValue((String)guardianAD[3]);
				nk1.getAddress(i).getCountry().setValue((String)guardianAD[4]);
			}
			
			for(int i=0; i<guardianTelephoneNumberDetails.size(); i++){
				nk1.getPhoneNumber(i).getTelephoneNumber().setValue((String)guardianTelephoneNumberDetails.get(i));
			}
			
			for(int i=0; i<guardianEmailAddressDetails.size(); i++){
				nk1.getPhoneNumber(i).getEmailAddress().setValue((String)guardianEmailAddressDetails.get(i));
			}
			nk1.getRelationship().getText().setValue(guardianRelationship);
			message=msg.getMessage().encode();
		}
		else{
			HL7Exception expection=new HL7Exception("patient does not have a guardian");
			message=qryMsg.generateACK("AR", expection).encode();
		}
		return message;
	}
	
	private String createResponseBhtDetails(int patient_id) throws HL7Exception, IOException{
		String message=null;
		ADR_A19 msg=new ADR_A19();
		ADR_A19_QUERY_RESPONSE response=msg.insertQUERY_RESPONSE(0);
		
		MSH mshSegment = msg.getMSH();
		mshSegment.getFieldSeparator().setValue("|");
		mshSegment.getEncodingCharacters().setValue("^~\\&");
		mshSegment.getDateTimeOfMessage().getTime().setValue(Calendar.getInstance().getTime());
		mshSegment.getMessageType().getMsg1_MessageCode().setValue("ADR");
		mshSegment.getVersionID().getVersionID().setValue("2.5.1");
		mshSegment.getMessageType().getTriggerEvent().setValue("A19");
		
		Object[][] fieldsAndValuesAdmission={{"patient_id", patient_id}, {"status", "in"}};
		String[] requiredFieldsAdmission={"admission_id", "admission_date", "admitting_officer_id", "admission_ward", "admission_bed_no", "admit_reason"};
		List admission=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesAdmission, requiredFieldsAdmission, "Admission", "admission");
		if(admission.size()==1){
			Object[] admission_detials=(Object[])admission.get(0);
			PID pid=response.getPID();
			pid.getPatientID().getIDNumber().setValue(String.valueOf(patient_id));
			
			PV1 pv1=response.getPV1();
			pv1.getAdmitDateTime().getTime().setValue((Date)admission_detials[1]);
			pv1.getAdmittingDoctor(0).getIDNumber().setValue((String)admission_detials[2]);
			pv1.getPriorPatientLocation().getBed().setValue(String.valueOf((Integer)admission_detials[4]));
			pv1.getPriorPatientLocation().getRoom().setValue(String.valueOf((Integer)admission_detials[3]));
			
			PV2 pv2=response.getPV2();
			pv2.getAdmitReason().getText().setValue((String)admission_detials[5]);
			
			Object[][] fieldsAndValuesBht={{"admission_id",  admission_detials[0]}};
			String[] requiredFieldsBht={"bht_id", "consultant_id", "current_ward", "current_bed"};
			List bht=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesBht, requiredFieldsBht, "Bht", "bht");
			Object[] bht_detials=(Object[])bht.get(0);
			pv1.getAssignedPatientLocation().getRoom().setValue(String.valueOf((Integer)bht_detials[2]));
			pv1.getAssignedPatientLocation().getBed().setValue(String.valueOf((Integer)bht_detials[3]));
			pv1.getConsultingDoctor(0).getIDNumber().setValue((String)bht_detials[1]);
			
			Object[][] fieldsAndValuesBhtEntry={{"bht_id", bht_detials[0]}};
			String[] requiredFieldsBhtEntry={"description_observation", "description_diagnosis", "entered_officer_id", "entered_time"};
			List bht_entry=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesBhtEntry, requiredFieldsBhtEntry, "BhtEntry", "bht_entry");
			int obsevation_repitition_counter=0;
			int diagnosis_repitition_counter=0;
			for(int i=0; i<bht_entry.size(); i++){
				Object[] bht_entry_detials=(Object[])bht_entry.get(i);
				if(!bht_entry_detials[0].equals("")){
					OBX obx=response.getOBX(obsevation_repitition_counter);
					obx.getValueType().setValue("ST");
					ST st=new ST(msg);
					st.setValue((String)bht_entry_detials[0]);
					obx.getObservationValue(0).setData(st);
					obx.getResponsibleObserver(0).getIDNumber().setValue((String)bht_entry_detials[2]);
					obx.getDateTimeOfTheObservation().getTime().setValue((Date)bht_entry_detials[3]);
					obsevation_repitition_counter++;
				}
				if(!bht_entry_detials[1].equals("")){
					DG1 dg1=response.getDG1(diagnosis_repitition_counter);
					dg1.getDiagnosisDescription().setValue((String)bht_entry_detials[1]);
					dg1.getDiagnosingClinician(0).getIDNumber().setValue((String)bht_entry_detials[2]);
					dg1.getDiagnosisDateTime().getTime().setValue((Date)bht_entry_detials[3]);
					diagnosis_repitition_counter++;
				}
			}
			message=msg.getMessage().encode();
		}
		else{
			HL7Exception expection=new HL7Exception("patient is not currently admitted to the hospital");
			message=qryMsg.generateACK("AR", expection).encode();
		}
		return message;
	}
	
	private String createResponseAdmissionDetails(int patient_id, String status) throws HL7Exception, IOException{
		String message=null;
		ADR_A19 msg=new ADR_A19();
		
		MSH mshSegment = msg.getMSH();
		mshSegment.getFieldSeparator().setValue("|");
		mshSegment.getEncodingCharacters().setValue("^~\\&");
		mshSegment.getDateTimeOfMessage().getTime().setValue(Calendar.getInstance().getTime());
		mshSegment.getMessageType().getMsg1_MessageCode().setValue("ADR");
		mshSegment.getVersionID().getVersionID().setValue("2.5.1");
		mshSegment.getMessageType().getTriggerEvent().setValue("A19");
		
		Object[][] fieldsAndValuesAdmission={{"patient_id", patient_id}, {"status", status}};
		String[] requiredFieldsAdmission={"admission_id", "admission_date", "admitting_officer_id", "admission_ward", "admission_bed_no", "admit_reason"};
		List admission=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesAdmission, requiredFieldsAdmission, "Admission", "admission");
		if(admission.size()>0){
			for(int k=0; k<admission.size(); k++){
				ADR_A19_QUERY_RESPONSE response=msg.insertQUERY_RESPONSE(k);
				Object[] admission_detials=(Object[])admission.get(k);
				PID pid=response.getPID();
				pid.getPatientID().getIDNumber().setValue(String.valueOf(patient_id));
				PV1 pv1=response.getPV1();
				pv1.getAdmitDateTime().getTime().setValue((Date)admission_detials[1]);
				pv1.getAdmittingDoctor(0).getIDNumber().setValue((String)admission_detials[2]);
				pv1.getPriorPatientLocation().getBed().setValue(String.valueOf((Integer)admission_detials[4]));
				pv1.getPriorPatientLocation().getRoom().setValue(String.valueOf((Integer)admission_detials[3]));
				PV2 pv2=response.getPV2();
				pv2.getAdmitReason().getText().setValue((String)admission_detials[5]);
				
				Object[][] fieldsAndValuesBht={{"admission_id",  admission_detials[0]}};
				String[] requiredFieldsBht={"bht_id", "consultant_id", "current_ward", "current_bed"};
				List bht=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesBht, requiredFieldsBht, "Bht", "bht");
				Object[] bht_detials=(Object[])bht.get(0);
				pv1.getAssignedPatientLocation().getBed().setValue(String.valueOf((Integer)bht_detials[3]));
				pv1.getAssignedPatientLocation().getRoom().setValue(String.valueOf((Integer)bht_detials[2]));
				pv1.getConsultingDoctor(0).getIDNumber().setValue((String)bht_detials[1]);
				
				if(status.equals("out")){
					Object[][] fieldsAndValuesDishcarge={{"admission_id", admission_detials[0]}};
					String[] requiredFieldDischarge={ "discharge_date", "comments"};
					List discharge=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesDishcarge, requiredFieldDischarge, "Discharge", "discharge");
					if(discharge.size()!=0){
						Object[] discharge_details=(Object[])discharge.get(0);
						pv1.getDischargeDateTime(0).getTime().setValue((Date)discharge_details[0]);
						pv1.getDischargeDisposition().setValue((String)discharge_details[1]);
					}else{
						Object[][] fieldsAndValuesTransfer={{"admissionId", admission_detials[0]}};
						String[] requiredFieldTransfer={"toHospital", "transferringOfficerId", "reason", "transferTime"};
						List transfer=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesTransfer, requiredFieldTransfer, "TransferToAnotherHospital", "transfer");
						if(transfer.size()!=0){
							Object[] transfer_details=(Object[])transfer.get(0);
							String x="Transfered to "+((String)transfer_details[0])+" due to "+((String)transfer_details[2])+" by "+((String)transfer_details[1]);
							pv1.getDischargeDisposition().setValue(x);
							pv1.getDischargeDateTime(0).getTime().setValue((Date)transfer_details[3]);	
						}
					}
				}
				
				Object[][] fieldsAndValuesBhtEntry={{"bht_id", bht_detials[0]}};
				String[] requiredFieldsBhtEntry={"description_observation", "description_diagnosis", "entered_officer_id", "entered_time"};
				List bht_entry=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesBhtEntry, requiredFieldsBhtEntry, "BhtEntry", "bht_entry");
				int obsevation_repitition_counter=0;
				int diagnosis_repitition_counter=0;
				for(int i=0; i<bht_entry.size(); i++){
					Object[] bht_entry_detials=(Object[])bht_entry.get(i);
					if(!bht_entry_detials[0].equals("")){
						OBX obx=response.getOBX(obsevation_repitition_counter);
						obx.getValueType().setValue("ST");
						ST st=new ST(msg);
						st.setValue((String)bht_entry_detials[0]);
						obx.getObservationValue(0).setData(st);
						obx.getResponsibleObserver(0).getIDNumber().setValue((String)bht_entry_detials[2]);
						obx.getDateTimeOfTheObservation().getTime().setValue((Date)bht_entry_detials[3]);
						obsevation_repitition_counter++;
					}
					if(!bht_entry_detials[1].equals("")){
						DG1 dg1=response.getDG1(diagnosis_repitition_counter);
						dg1.getDiagnosisDescription().setValue((String)bht_entry_detials[1]);
						dg1.getDiagnosingClinician(0).getIDNumber().setValue((String)bht_entry_detials[2]);
						dg1.getDiagnosisDateTime().getTime().setValue((Date)bht_entry_detials[3]);
						diagnosis_repitition_counter++;
					}
				}
				Object[][] fieldsAndValuesGuardian={{"patient_id", patient_id}, {"status", status}};
				int guardian_id=hibernate_manager.queryValuesFromDatabase(fieldsAndValuesGuardian, "guardian_id", "Admission", "admission");
				if(guardian_id!=-1){
					Object[][] fieldsAndValues={{"guardian_id", guardian_id}};
					String[] requiredFieldsGuardian={"prefix", "given_name", "further_names", "surname", "nic_no"};
					String[] required_fieldAddress={ "house_number", "street_name", "city_1", "city_2", "country"};
					String[] required_fieldTelephone={"telephone_number"};
					String[] required_fieldEmail={ "email_address"};
					List guardian=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, requiredFieldsGuardian, "Guardian", "guardian");
					List guardianAddressDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldAddress, "GuardianAddressDetails", "guardian_address_detials");
					List guardianEmailAddressDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldEmail, "GuardianEmailAddressDetails", "guardian_email_address_detials");
					List guardianTelephoneNumberDetails=hibernate_manager.queryValueListFromDatabase(fieldsAndValues, required_fieldTelephone, "GuardianTelephoneNumberDetails", "guardian_telephone_number_details");
					String guardianRelationship=this.getGuardianRelationship(guardian_id, patient_id);

					NK1 nk1=response.getNK1();
					Object[] guardianDetails=(Object[])guardian.get(0);
					nk1.getNK1Name(0).getPrefixEgDR().setValue((String)guardianDetails[0]);
					nk1.getNK1Name(0).getGivenName().setValue((String)guardianDetails[1]);
					nk1.getNK1Name(0).getSecondAndFurtherGivenNamesOrInitialsThereof().setValue((String)guardianDetails[2]);
					nk1.getNK1Name(0).getFamilyName().getSurname().setValue((String)guardianDetails[3]);
					nk1.getContactPersonSocialSecurityNumber().setValue((String)guardianDetails[4]);
					
					for(int i=0; i<guardianAddressDetails.size(); i++){
						Object[] guardianAD=(Object[])guardianAddressDetails.get(i);
						nk1.getAddress(i).getStreetAddress().getDwellingNumber().setValue((String)guardianAD[0]);
						nk1.getAddress(i).getStreetAddress().getStreetName().setValue((String)guardianAD[1]);
						nk1.getAddress(i).getOtherDesignation().setValue((String)guardianAD[2]);
						nk1.getAddress(i).getCity().setValue((String)guardianAD[3]);
						nk1.getAddress(i).getCountry().setValue((String)guardianAD[4]);
					}
					
					for(int i=0; i<guardianTelephoneNumberDetails.size(); i++){
						nk1.getPhoneNumber(i).getTelephoneNumber().setValue((String)guardianTelephoneNumberDetails.get(i));
					}
					
					for(int i=0; i<guardianEmailAddressDetails.size(); i++){
						nk1.getPhoneNumber(i).getEmailAddress().setValue((String)guardianEmailAddressDetails.get(i));
					}
					nk1.getRelationship().getText().setValue(guardianRelationship);
				}
			}
			message=msg.getMessage().encode();
		}
		else{
			HL7Exception expection=null;
			if(status.equals("out")){
				expection=new HL7Exception("No previous admissions of the patient");
			}else if(status.equals("in")){
				expection=new HL7Exception("Patient is not currently admited");
			}
			message=qryMsg.generateACK("AR", expection).encode();
		}
		return message;
	}
	
	private String createResponseForOccupiedBeds(int ward) throws HL7Exception{
		String message=null;
		ADR_A19 msg=new ADR_A19();
		
		MSH mshSegment = msg.getMSH();
		mshSegment.getFieldSeparator().setValue("|");
		mshSegment.getEncodingCharacters().setValue("^~\\&");
		mshSegment.getDateTimeOfMessage().getTime().setValue(Calendar.getInstance().getTime());
		mshSegment.getMessageType().getMsg1_MessageCode().setValue("ADR");
		mshSegment.getVersionID().getVersionID().setValue("2.5.1");
		mshSegment.getMessageType().getTriggerEvent().setValue("A19");
		
		Object[][][] fieldsAndValues={{{"current_ward", ward}},{{"status", "in"}}};
		String[] requiredClassAndField={"bht", "current_bed"};
		String[][] classAndObjectNames={{"Bht", "bht"},{"Admission", "admission"}};
		String[][] joinObjectAndField={{"bht", "admission_id"},{"admission", "admission_id"}};
		List bht=hibernate_manager.queryUsingJoin(classAndObjectNames, fieldsAndValues, requiredClassAndField, joinObjectAndField);
		String occupieBeds="";
		for(int i=0; i<bht.size(); i++){
			occupieBeds=occupieBeds+String.valueOf(bht.get(i))+",";
		}
		msg.getMSA().getAcknowledgmentCode().setValue("AA");
		msg.getMSA().getTextMessage().setValue(occupieBeds);
		message=msg.getMessage().encode();
		return message;
	}
	
	private String getGuardianRelationship(int guardian_id, int patient_id){
		String relationship=null;
		Object[][] fieldsAndValues={{"patient_id", patient_id},{"guardian_id", guardian_id}};
		int guardian_relationship_id=hibernate_manager.queryValuesFromDatabase(fieldsAndValues, "guardian_relationship_id", "Admission", "admission");
		if(guardian_relationship_id!=-1){
			Object[][] fieldsAndValuesRelationship={{"guardian_relationship_id", guardian_relationship_id}};
			relationship = hibernate_manager.getRelationship(guardian_relationship_id);
		}
		return relationship;
	}
	
	private int getPatientID(int ward, int bed){
		int patientId=-1;
		Object[][] fieldsAndValuesBht={{"current_ward",  ward}, {"current_bed",  bed}};
		String[] requiredFieldsBht={"admission_id"};
		Object[][][] fieldsAndValues={{{"current_ward", ward},{"current_bed",  bed} },{{"status", "in"}}};
		String[] requiredClassAndField={"admission", "admission_id"};
		String[][] classAndObjectNames={{"Bht", "bht"},{"Admission", "admission"}};
		String[][] joinObjectAndField={{"bht", "admission_id"},{"admission", "admission_id"}};
		List bht=hibernate_manager.queryUsingJoin(classAndObjectNames, fieldsAndValues, requiredClassAndField, joinObjectAndField);
//		List bht=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesBht, requiredFieldsBht, "Bht", "bht");
		if(bht.size()==1){
			int admissionId=(Integer)bht.get(0);
			Object[][] fieldsAndValuesAdmission={{"admission_id", admissionId}};
			String[] requiredFieldsAdmission={"patient_id"};
			List admission=hibernate_manager.queryValueListFromDatabase(fieldsAndValuesAdmission, requiredFieldsAdmission, "Admission", "admission");
			if(admission.size()==1){
				patientId=(Integer)admission.get(0);
			}
		}
		return patientId;
	}
}
	